// Package proto 包含了app端与配置服务传输协议中定义的数据封装处理.
package proto

import (
	"acs/pbmodel"
	"encoding/binary"
)

const (
	// VER 协议版本
	VER = uint8(1)
	// VerLength 版本号数据长度(字节)
	VerLength = 1
	// HeadLength 数据长度标识区域占的长度(字节)
	HeadLength = 4
	// TsidTypeLen 事务ID(包括包类型)表示区域占的长度(字节)
	TsidTypeLen = 2
)

const (
	CmdPing      = "ping"
	CmdRegister  = "register"
	CmdEvent     = "event"
	CmdPatch     = "patch"
	CmdPathfin   = "patchfin"
	CmdSchema    = "schema"
	CmdSchemafin = "schemafin"
	CmdForward   = "forwardhttp"
)

const (
	CmdErrCodeUnknownCmd    = 1000
	CmdErrCodeDataDecodeErr = 1001
	CmdHandledSuccess       = 2000
	CmdStatusOk             = 6000
)

type Request struct {
	// 只包括cmd和Data的长度
	Length uint32
	// 事务ID
	Tsid uint16
	Cmd  string
	// 参数数据
	Data []byte
	EncryptCfg
}

type EncryptCfg struct {
	EncryptKey string

	EncryptIV string
}
type Response struct {
	// 只包括Data的长度
	Length uint32
	// 事务ID
	Tsid uint16
	// 处理结果数据
	Data []byte
	EncryptCfg
}

// ProtoError 包括协议及系统处理层的错误，和业务无关。
type ProtoError struct {
	// 只包括Data的长度
	Length uint32
	// 事务ID
	Tsid uint16
	// 错误相关的描述内容
	Data []byte
	EncryptCfg
}

type PacketType uint16

const (
	PacketTypeRequest PacketType = iota
	PacketTypeResponse
	PacketTypeProtoError
)

type Packet struct {
	Type     PacketType
	Req      *Request
	Resp     *Response
	ProtoErr *ProtoError
}

// ClientInfo 为当前已经连接到配置服务的客户端信息
type ClientInfo struct {
	RegisterInfo pbmodel.RegisterInfo
	ConnectTime  int64
	LastRegTime  int64
	IpAddr       string
}

func NewProtoError(transactionId uint16, data []byte, encryptKey, encryptIV string) *ProtoError {
	return &ProtoError{
		Tsid:       transactionId,
		Data:       data,
		EncryptCfg: EncryptCfg{EncryptKey: encryptKey, EncryptIV: encryptIV},
	}
}

// Encode 根据协议生成协议相关错误的数据包.
func (e *ProtoError) Encode() []byte {
	var err error
	e.Data, err = Encrypt(e.Data, e.EncryptKey, e.EncryptIV)
	if err != nil {
		panic("proto encode error: " + err.Error())
	}
	e.Length = uint32(len(e.Data))
	packet := make([]byte, HeadLength+1+TsidTypeLen+e.Length)
	binary.BigEndian.PutUint32(packet, e.Length)
	packet[HeadLength] = VER
	// 设置事务ID及包类型
	binary.BigEndian.PutUint16(packet[HeadLength+1:], e.Tsid<<2+2)
	copy(packet[HeadLength+1+TsidTypeLen:], e.Data)
	return packet
}

func NewResponse(transactionId uint16, data []byte, encryptKey, encryptIV string) *Response {
	return &Response{
		Tsid:       transactionId,
		Data:       data,
		EncryptCfg: EncryptCfg{EncryptKey: encryptKey, EncryptIV: encryptIV},
	}
}

// Encode 根据协议生成响应数据包.
func (r *Response) Encode() []byte {
	var err error
	r.Data, err = Encrypt(r.Data, r.EncryptKey, r.EncryptIV)
	if err != nil {
		panic("proto encode error: " + err.Error())
	}
	r.Length = uint32(len(r.Data))

	// 其中 1为版本号位.
	packet := make([]byte, HeadLength+1+TsidTypeLen+r.Length)
	binary.BigEndian.PutUint32(packet, r.Length)
	packet[HeadLength] = VER
	// 设置事务ID及包类型
	binary.BigEndian.PutUint16(packet[HeadLength+1:], r.Tsid<<2+1)
	copy(packet[HeadLength+1+TsidTypeLen:], r.Data)
	return packet
}

func NewRequest(transactionId uint16, cmd string, data []byte, encryptKey, encryptIV string) *Request {
	return &Request{
		Tsid:       transactionId,
		Cmd:        cmd,
		Data:       data,
		EncryptCfg: EncryptCfg{EncryptKey: encryptKey, EncryptIV: encryptIV},
	}
}

// Encode 根据协议生成请求数据包.
func (r *Request) Encode() []byte {
	var err error
	r.Data, err = Encrypt(r.Data, r.EncryptKey, r.EncryptIV)
	if err != nil {
		panic("proto encode error: " + err.Error())
	}
	// 根据协议: cmd以"\n"结尾, 需算上结尾符的长度.
	r.Length = uint32(len(r.Cmd) + 1 + len(r.Data))
	packet := make([]byte, HeadLength+1+TsidTypeLen+r.Length)
	binary.BigEndian.PutUint32(packet, r.Length)
	packet[HeadLength] = VER
	// 设置事务ID及包类型
	binary.BigEndian.PutUint16(packet[HeadLength+1:], r.Tsid<<2)
	n := copy(packet[HeadLength+1+TsidTypeLen:], []byte(r.Cmd+"\n"))
	copy(packet[HeadLength+1+TsidTypeLen+n:], r.Data)
	return packet
}

func (r *Request) GetTsid() uint16 {
	return r.Tsid
}

func (*Request) GetType() PacketType {
	return PacketTypeRequest
}
